import sys
from PyQt5.QtWidgets import *
import serial
import serial.tools.list_ports
import re
import os

import numpy as np

SOH = 0x01
STX = 0x02
EOT = 0x04
ACK = 0x06
NAK = 0x15
C = b'C'

# For CRC algorithm
CRC_TA16 = [0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
            0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef]


class Ui_mainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(400, 200)
        self.setWindowTitle("串口IAP")

        self.CtlWidget()
        self.Init()

        # 布局
        self.hbox = QHBoxLayout(self)
        self.hbox.addWidget(self.ctlwidget, 4)
        self.setLayout(self.hbox)
        self.show()

    # ##### 初始化操作 #####
    def Init(self):
        self.tab1 = []
        self.tab2 = []
        self.Refresh_Btn()
        self.ser = serial.Serial()

    # ##### 左侧控制界面 #####
    def CtlWidget(self):
        self.ctlwidget = QStackedWidget(self)  # 创建窗体栈空间

        self.SetGroup()

        self.progressbar = QProgressBar(self)
        self.progressbar.setMinimum(0)  # 将范围设为0-100
        self.progressbar.setMaximum(100)
        self.progressbar.setValue(0)

        self.btnlabel = QLabel(self)
        self.btnlabel.setText("Tips: ")

        self.total_layout = QGridLayout()
        self.total_layout.addWidget(self.SetGroup1, 0, 0, 1, 1)
        self.total_layout.addWidget(self.SetGroup2, 0, 1, 1, 1)
        self.total_layout.addWidget(self.btnlabel, 1, 0, 1, 1)
        self.total_layout.addWidget(self.progressbar, 1, 1, 1, 1)

        self.task = QWidget()
        self.task.setLayout(self.total_layout)
        self.ctlwidget.addWidget(self.task)

    def SetGroup(self):
        # 插件
        self.SetGroup1 = QGroupBox("串口控制")
        self.ctl_cb = QComboBox(self)
        self.ctl_cb.setFixedSize(70, 30)

        self.refreshbtn = QPushButton("刷新")
        self.refreshbtn.setFixedSize(70, 30)
        self.refreshbtn.clicked.connect(lambda: self.Refresh_Btn())

        self.connectbtn = QPushButton("连接")
        self.connectbtn.setFixedSize(70, 30)
        self.connectbtn.clicked.connect(lambda: self.Connect_Btn())

        self.closebtn = QPushButton("断开")
        self.closebtn.setFixedSize(70, 30)
        self.closebtn.clicked.connect(lambda: self.Close_Btn())
        # 布局
        self.grid_layout = QGridLayout()
        self.grid_layout.addWidget(self.ctl_cb, 0, 0, 1, 1)
        self.grid_layout.addWidget(self.refreshbtn, 0, 1, 1, 1)
        self.grid_layout.addWidget(self.connectbtn, 1, 0, 1, 1)
        self.grid_layout.addWidget(self.closebtn, 1, 1, 1, 1)
        self.SetGroup1.setLayout(self.grid_layout)

        # 右侧插件
        self.SetGroup2 = QGroupBox("数据传输")

        self.filename = QLineEdit(self)
        self.filename.setDisabled(False)
        self.filename.setText("文件名")

        self.loadbtn = QPushButton("加载文件")
        self.loadbtn.setFixedSize(70, 30)
        self.loadbtn.clicked.connect(lambda: self.Load_Btn())

        self.downloadbtn = QPushButton("下载")
        self.downloadbtn.setFixedSize(70, 30)
        self.downloadbtn.setDisabled(True)
        self.downloadbtn.clicked.connect(lambda: self.Download_Btn())

        self.file_layout = QGridLayout()
        self.file_layout.addWidget(self.filename, 0, 0, 1, 2)
        self.file_layout.addWidget(self.loadbtn, 1, 0, 1, 1)
        self.file_layout.addWidget(self.downloadbtn, 1, 1, 1, 1)

        self.SetGroup2.setLayout(self.file_layout)

    # 刷新搜索串口
    def Refresh_Btn(self):
        self.ctl_cb.clear()
        port_list = list(serial.tools.list_ports.comports())
        com_numbers = len(port_list)
        p1 = re.compile(r'[(](.*?)[)]', re.S)
        for i in range(com_numbers):
            com_list = str(port_list[i])
            com_name = re.findall(p1, com_list)
            com_name = str(com_name)
            strlist = com_name.split("'")
            self.ctl_cb.addItem(strlist[1])


    # 连接串口接收数据
    def Connect_Btn(self):
        temp = (self.ctl_cb.currentText(), 115200, 8, 1, "N")
        self.ser = serial.Serial(port=self.ctl_cb.currentText(), baudrate=115200, bytesize=8, parity='N', stopbits=1)
        if self.ser.isOpen():
            print("%s opened success" % (self.ctl_cb.currentText()))
            self.ctl_cb.setDisabled(True)
            self.refreshbtn.setDisabled(True)
            self.connectbtn.setDisabled(True)
            self.btnlabel.setText("Tips: 串口连接成功！")
            if self.filename.text() != "文件名" and self.filename.text() != "":
                self.downloadbtn.setDisabled(False)
        else:
            print("%s opened fail" % (self.ctl_cb.currentText()))
            self.btnlabel.setText("Tips: 串口连接失败！")

    def Close_Btn(self):
        self.ser.close()
        if self.ser.isOpen():
            print("%s closed fail" % (self.ctl_cb.currentText()))
        else:
            self.ctl_cb.setDisabled(False)
            self.refreshbtn.setDisabled(False)
            self.connectbtn.setDisabled(False)
            self.downloadbtn.setDisabled(True)
            print("%s closed success" % (self.ctl_cb.currentText()))
            self.btnlabel.setText("Tips: 串口已关闭！")

    def Load_Btn(self):
        name = QFileDialog.getOpenFileName(self,'选择文件','','Bin files(*.bin)')
        self.openfile_name = name[0]
        filename = []
        self.file_size = os.path.getsize(self.openfile_name)
        i = 0
        file_name = []
        while True:
            i = i + 1
            if self.openfile_name[-i] == '/':
                break
            else:
                file_name.extend(self.openfile_name[-i])
        for item in reversed(file_name):
            filename.append(item)

        self.filename.setText(''.join(filename) + "(%d)" % (self.file_size))
        print(self.openfile_name, self.file_size)
        self.btnlabel.setText("Tips: 已加载bin文件！")

        if self.ser.isOpen():
            self.downloadbtn.setDisabled(False)
        else:
            self.downloadbtn.setDisabled(True)

    def bytesToInt(self, bytes):
        result = 0
        for b in bytes:
            result = result * 256 + int(b)
        return result

    def intToBytes(self, value, length):
        result = []
        for i in range(0, length):
            result.append(value >> (i * 8) & 0xff)
        result.reverse()
        return result

    def crc16(self, p_Data):
        crc = 0
        for x in p_Data:
            da = ((crc >> 8) & 0xFF) >> 4
            crc <<= 4
            crc ^= CRC_TA16[da ^ (x >> 4)]
            da = ((crc >> 8) & 0xFF) >> 4
            crc <<= 4
            crc ^= CRC_TA16[da ^ (x & 0x0f)]
            crc = crc & 0xffff
        crch = (crc>>8)
        crcl = (crc&0xFF)
        return crch, crcl


    def Download_Btn(self):
        if self.ser.isOpen():
            print("serial opened success")
        else:
            print("serial opened fail")
        # 检查Ymodem
        while True:
            cc = self.ser.read(1)
            if cc == C:
                break

        self.btnlabel.setText("Tips: 下载中 ......")
        # 发送起始帧
        s1 = [SOH, 0, 0xFF]
        s2 = [0x6C, 0x65, 0x64, 0x2E, 0x62, 0x69, 0x6E, 0x00, 0x33, 0x31, 0x30, 0x30, 0x20, 0x31, 0x34, 0x30,0x30, 0x31, 0x36, 0x30, 0x35, 0x33, 0x33, 0x34, 0x20, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34] + [0] * 97
        s2.extend(self.crc16(s2))
        self.ser.write(s1 + s2)
        while True:
            # 等待回应
            cc = self.ser.read(1)
            if cc == b'\x06':
                break

        # 读文件
        file = open(self.openfile_name, 'rb')
        j = 0
        breadstatus = 0
        while True:
            j += 1
            aa = [SOH, j, 255-j]
            cc = []
            for i in range(128):
                rf = file.read(1)
                if rf == b'':
                    rf = b'\x1A'
                    breadstatus = 1
                cc.extend(rf)
            cc.extend(self.crc16(cc))
            self.ser.write(aa + cc)
            # print(aa + cc)
            load = 128*j*100//self.file_size
            if load > 100:
                load = 100
            self.progressbar.setValue(load)
            if breadstatus == 1:
                break
            while True:
                if self.ser.read(1) == b'\x06':
                    break

        while True:
            self.ser.write(b'\x04')
            cc = self.ser.read(1)
            if cc == b'\x15':
                break

        while True:
            self.ser.write(b'\x04')
            cc = self.ser.read(1)
            if cc == b'\x06':
                break

        # 发送结束帧
        s1 = [SOH, 0, 0xFF]
        s2 = [0] * 128
        s2.extend(self.crc16(s2))
        self.ser.write(s1 + s2)
        while True:
            # 等待回应
            cc = self.ser.read(1)
            if cc == b'\x06':
                break

        self.btnlabel.setText("Tips: 下载完成！")


if __name__ == '__main__':  # 程序入口
    app = QApplication(sys.argv)
    form = Ui_mainWindow()
    sys.exit(app.exec_())
